
package com.hlkj.pay.app.merchant.ext.impl.wxpay.handler;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import com.hlkj.framework.caches.service.RedisTemplateService;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.common.impl.UploadServiceImpl;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.constant.WxPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.dto.WxPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.dto.WxPlatformConfig;
import com.hlkj.pay.common.PayResultCode;
import com.hlkj.pay.dto.merchant.MerchantAccessScene;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.infrastructure.model.common.CommonAppDO;
import com.hlkj.pay.infrastructure.model.order.OrderComplaintChannelDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.service.common.ICommonAppConfigService;
import com.hlkj.pay.util.ImageUtils;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.AuthTypeEnum;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.OtherApiEnum;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 微信服务商 商户进件参数处理
 * 
 * @author HlpayTeam
 * @date 2024/09/26 12:02
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WxPayCommonParamsHandle {

    private final UploadServiceImpl uploadService;

    private final RedisTemplateService redisTemplateService;

    private final ICommonAppConfigService commonAppConfigService;

    @Value("${spring.profiles.active:}")
    private String active;


    public String subject(String subject) throws UnsupportedEncodingException {
        return URLEncoder.encode(subject, "GBK");
    }

    public CommonResult<WxPayConfig> wxComplaintConfig(OrderComplaintChannelDO orderComplaintChannelDO, boolean test) {
        String paramsConfig = orderComplaintChannelDO.getBaseConfig();
        if (StringUtils.isEmpty(paramsConfig)) {
            return CommonResult.error(PayResultCode.PAY_CHANNEL_CONFIG_NOT_EXITS);
        }

        // 配置转换
        WxPayConfig wxPayConfig = null;
        try {
            wxPayConfig = JsonUtils.parseObject(paramsConfig, WxPayConfig.class);
            WxPlatformConfig platformConfig = getWxPlatformConfig(wxPayConfig);
            if(platformConfig != null){
                wxPayConfig.setPlatformCertPath(platformConfig.getPlatformCertPath());
            }else{
                // 默认证书地址
                String platformCertPath = null;
                if(active.equals("local")){
                    // 判断是否带盘符
                    if(wxPayConfig.getPrivateKeyPath().contains("\\")){
                        platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"\\",true)+"\\"+ IdUtil.fastSimpleUUID()+"_" +WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                    }else{
                        platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"/",true)+"/"+ IdUtil.fastSimpleUUID() +"_"+WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                    }
                }else{
                    platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"/",true)+"/"+ IdUtil.fastSimpleUUID()+"_" +WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                }
                wxPayConfig.setPlatformCertPath(platformCertPath);
            }

            if(active.equals("local")){
                log.info("微信非prod 环境,更新证书信息");
                wxPayConfig.setPrivateKeyPath(wxPayConfig.getPrivateKeyPath().replace("/www/wwwroot/pay/uploads/secret/20250320/","E:\\work\\local_file\\secret\\20250320\\"));
                wxPayConfig.setPlatformCertPath(wxPayConfig.getPlatformCertPath().replace("/www/wwwroot/pay/uploads/secret/20250320/","E:\\work\\local_file\\secret\\20250320\\"));
                wxPayConfig.setPayPublicPath(wxPayConfig.getPayPublicPath().replace("/www/wwwroot/pay/uploads/secret/20250320/","E:\\work\\local_file\\secret\\20250320\\"));
            }
            return CommonResult.success(wxPayConfig);
        }
        catch (Exception e) {
            String format = String.format(PayResultCode.PAY_CHANNEL_CONFIG_ERROR.getMsg(), "微信");
            return CommonResult.error(PayResultCode.PAY_CHANNEL_CONFIG_NOT_EXITS.getCode(), format);
        }
    }


    /**
     * 获取appid
     * @param providerChannelConfigDO
     * @param appType
     * @return
     */
    public String getAppid(ProviderChannelConfigDO providerChannelConfigDO, CommonEnum.COMMON_APP_TYPE appType) {
        Long id = null;
        switch (appType) {
            case WECHAT_PUB:
                id =  providerChannelConfigDO.getWechatDefaultPubId();
                break;
            case WECHAT_MIN:
                id =  providerChannelConfigDO.getWechatDefaultMiniId();
                break;
        }
        if (id == null) {
            return null;
        }
        CommonAppDO commonAppDO = commonAppConfigService.detail(id);
        if (commonAppDO == null){
            return null;
        }
        return commonAppDO.getAppId();
    }

    public CommonResult<WxPayConfig> wxPayConfig(ProviderChannelConfigDO providerChannelConfigDO, boolean test) {
        String paramsConfig = providerChannelConfigDO.getParamsConfig();
        if (StringUtils.isEmpty(paramsConfig)) {
            return CommonResult.error(PayResultCode.PAY_CHANNEL_CONFIG_NOT_EXITS);
        }
        // 配置转换
        WxPayConfig wxPayConfig = null;
        try {
            wxPayConfig = JsonUtils.parseObject(paramsConfig, WxPayConfig.class);
            WxPlatformConfig platformConfig = getWxPlatformConfig(wxPayConfig);
            if(platformConfig != null){
                wxPayConfig.setPlatformCertPath(platformConfig.getPlatformCertPath());
            }else{
                // 默认证书地址
                String platformCertPath = null;
                if(active.equals("local")){
                    // 判断是否带盘符
                    if(wxPayConfig.getPrivateKeyPath().contains("\\")){
                        platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"\\",true)+"\\"+ IdUtil.fastSimpleUUID()+"_" +WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                    }else{
                        platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"/",true)+"/"+ IdUtil.fastSimpleUUID() +"_"+WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                    }
                }else{
                    platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"/",true)+"/"+ IdUtil.fastSimpleUUID()+"_" +WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                }
                wxPayConfig.setPlatformCertPath(platformCertPath);
            }
            if(active.equals("local")){
                log.info("微信非prod 环境,更新证书信息");
                wxPayConfig.setPrivateKeyPath(wxPayConfig.getPrivateKeyPath().replace("/www/wwwroot/pay/uploads/secret/20250306/","E:\\work\\local_file\\secret\\20250306\\"));
                wxPayConfig.setPlatformCertPath(wxPayConfig.getPlatformCertPath().replace("/www/wwwroot/pay/uploads/secret/20250305/","E:\\work\\local_file\\secret\\20250306\\"));
                wxPayConfig.setPayPublicPath(wxPayConfig.getPayPublicPath().replace("/www/wwwroot/pay/uploads/secret/20250319/","E:\\work\\local_file\\secret\\20250306\\"));
            }
            return CommonResult.success(wxPayConfig);
        }
        catch (Exception e) {
            String format = String.format(PayResultCode.PAY_CHANNEL_CONFIG_ERROR.getMsg(), "微信");
            return CommonResult.error(PayResultCode.PAY_CHANNEL_CONFIG_NOT_EXITS.getCode(), format);
        }
    }

    public static String date(String date) {
        if (!StringUtils.hasText(date)) {
            return null;
        }
        return date;
    }

    public static String licenseType(String code) {
        if (!StringUtils.hasText(code)) {
            return null;
        }
        // 营业执照类型：
        // 1：营业执照
        // 2：营业执照(多证合一)
        // 3：事业单位法人证书
        // 4：社会团体法人登记证书
        // 5：民办非企业单位登记证书
        // 6：基金会法人登记证书
        // 7：宗教活动场所登记证
        // 8：其他证书/批文/证明
        // 9：执业许可证/执业证
        // 10：基层群众性自治组织特别法人统一社会信用代码证
        // 11：农村集体经济组织登记证
        // 12：业主委员会备案证明


        // 11 营业执照
        // 12 组织机构代码证
        // 13 税务登记证
        // 14 统一社会信用代码证
        // 15 单位证明
        // 16 事业单位法人正式
        // 17 民办非企业单位登记证书
        // 18 基金会法人登记证书
        // 19 社会团体登记证书
        // 99 其它
        switch (code) {
            case "1":
            case "2":
                return "11";
            case "3":
                return "16";
            case "4":
                return "19";
            case "5":
                return "17";
            case "6":
                return "18";
            case "7":
                return "12";
            case "8":
                return "99";
            case "9":
                return "99";
            case "10":
                return "12";
            case "11":
                return "12";
            case "12":
                return "15";
            default:
                return null;
        }
    }

    public static String certType(String code) {
        if (!StringUtils.hasText(code)) {
            return null;
        }
        // 营业执照类型：
        // 1:身份证
        // 2:护照
        // 3:军官证
        // 4:士兵证
        // 5:回乡证
        // 6:户口本
        // 7:外国护照
        // 8:其他
        // 9:暂住证
        // 10:警官证
        // 11:文职干部证
        // 12:港澳台同胞通行证
        // 13:外国人居留证
        // 14:台胞证
        // 15:港澳台居住证
        // 16：营业执照 （只有部分场景才会用到 如 受益人和股东）


        /**
            IDENTIFICATION_TYPE_IDCARD: 中国大陆居民-身份证
            IDENTIFICATION_TYPE_OVERSEA_PASSPORT: 其他国家或地区居民-护照
            IDENTIFICATION_TYPE_HONGKONG_PASSPORT: 中国香港居民-来往内地通行证
            IDENTIFICATION_TYPE_MACAO_PASSPORT: 中国澳门居民-来往内地通行证
            DENTIFICATION_TYPE_TAIWAN_PASSPORT: 中国台湾居民-来往大陆通行证
            DENTIFICATION_TYPE_FOREIGN_RESIDENT: 外国人居留证
            DENTIFICATION_TYPE_HONGKONG_MACAO_RESIDENT: 港澳居民证
            IDENTIFICATION_TYPE_TAIWAN_RESIDENT: 台湾居民证
         */
        switch (code) {
            case "1":
                return "IDENTIFICATION_TYPE_IDCARD";
            case "7":
                return "IDENTIFICATION_TYPE_OVERSEA_PASSPORT";
            case "13":
                return "DENTIFICATION_TYPE_FOREIGN_RESIDENT";
            default:
                return null;
        }
    }

    /**
     * 微信进件主体类型
     * SUBJECT_TYPE_INDIVIDUAL : （个体户）营业执照上的主体类型一般为个体户、个体工商户、个体经营 <br/>
     * SUBJECT_TYPE_ENTERPRISE : （企业）营业执照上的主体类型一般为有限公司、有限责任公司
     * SUBJECT_TYPE_GOVERNMENT: （政府机关）包括各级、各类政府机关，如机关党委、税务、民政、人社、工商、商务、市监等
     * SUBJECT_TYPE_INSTITUTIONS: （事业单位）包括国内各类事业单位，如：医疗、教育、学校等单位
     * SUBJECT_TYPE_OTHERS :（政府机关） 包括社会团体、民办非企业、基金会、基层群众性自治组织、农村集体经济组织等组织
     *
     * 系统主体类型
     * 1：政府机构
     * 2：国营企业
     * 3：私营企业
     * 4：外资企业
     * 5：个体工商户
     * 6：其它组织
     * 7：事业单位
     * 8：业主委员会；
     */
    public static String companyType(Integer type) {
        String companyType = null;
        switch (type){
            case 1:
                companyType = "SUBJECT_TYPE_GOVERNMENT";
                break;
            case 2:
            case 3:
            case 4:
                companyType = "SUBJECT_TYPE_ENTERPRISE";
                break;
            case 5:
                companyType = "SUBJECT_TYPE_INDIVIDUAL";
                break;
            case 6:
            case 8:
                companyType = "SUBJECT_TYPE_OTHERS";
                break;
            case 7:
                companyType = "SUBJECT_TYPE_INSTITUTIONS";
                break;
        }

        return companyType;
    }


    public static String problemType(String type) {
        String problemType = null;
        switch (type){
            case "REFUND":
                problemType = "申请退款";
                break;
            case "SERVICE_NOT_WORK":
                problemType = "服务权益未生效";
                break;
            default:
                problemType = "其他类型";
                break;
        }
        return problemType;
    }

    /**
     * 过去指定场景类型的数据
     * @param sceneList
     * @param senceType
     * @return
     */
    public  MerchantAccessScene getMerchantAccessSceneValue(List<MerchantAccessScene> sceneList, Integer senceType){
        if(CollUtil.isNotEmpty(sceneList)){
            for(MerchantAccessScene accessScene:sceneList){
                if(accessScene.getType() == senceType){
                    return accessScene;
                }
            }

        }
        return null;
    }

    /**
     * 0：对公账户 1：对私法人账户 2：对私非法人账户
     * @param type
     * @return
     */
    public String getBankAccountType(Integer type){
        String bankAccountType = null;
        if(type == null){
            return bankAccountType;
        }
        switch (type){
            case 0:
                bankAccountType = "BANK_ACCOUNT_TYPE_CORPORATE";
                break;
            default:
                bankAccountType = "BANK_ACCOUNT_TYPE_PERSONAL";
                break;
        }
        return bankAccountType;
    }

    /**
     * 微信上传图片获取 media_id
     * @param filePath   本地路劲/https
     * @param wxPayConfig
     * @return
     */
    public  String getV3UploadFileMediaId(String filePath, WxPayConfig wxPayConfig) {

        try {

            File file = null;
            if(HttpUtil.isHttp(filePath)  || HttpUtil.isHttps(filePath) ){
                // 创建临时文件
                file =  FileUtil.createTempFile("."+ ImageUtils.getFileExtension(filePath),true);
                // 文件流copy到临时文件
                HttpUtil.downloadFile(filePath,file);
            }else{
                file = FileUtil.newFile(filePath);
            }

            if(FileUtil.isEmpty(file)){
                log.error("微信 V3Upload 上传文件格式异常 ");
                throw new RuntimeException("微信 V3Upload 上传文件格式异常");
            }

            HashMap<Object, Object> map = new HashMap<>();
            map.put("filename", file.getName());
            map.put("sha256", SecureUtil.sha256(file));
            String reqBody = JSONUtil.toJsonStr(map);

            IJPayHttpResponse response = WxPayApi.v3(
                    WxDomainEnum.CHINA.toString(),
                    OtherApiEnum.MERCHANT_UPLOAD_MEDIA.toString(),
                    wxPayConfig.getMchId(),
                    wxPayConfig.getSerialNo(),
                    null,
                    wxPayConfig.getPrivateKeyPath(),
                    reqBody,
                    file
            );
            log.info("微信 V3Upload 上传文件 filePath :{}, 配置 :{}, 返回参数 :{}",filePath, JSONUtil.toJsonStr(wxPayConfig),JSONUtil.toJsonStr(response));
            if(response.getStatus() == WxPayExtConstant.OK){
                JSONObject respBody = JSONUtil.parseObj(response.getBody());
                Object mediaId = respBody.get("media_id");
                if(mediaId != null){
                    return mediaId.toString();
                }
            }else{
                log.error("微信 V3Upload 上传文件 filePath :{}, 配置 :{}, 返回参数 :{}",filePath, JSONUtil.toJsonStr(wxPayConfig),JSONUtil.toJsonStr(response));
                throw new RuntimeException("微信 V3Upload 上传文件异常 : "+filePath+" , "+response.getBody());
            }
        } catch (Exception e) {
            log.error("微信 V3Upload 上传文件异常", e);
            throw new RuntimeException("微信 V3Upload 上传文件异常", e);
        }
        return null;
    }


    public  String getV3UploadFileMediaId(String filePath, WxPayConfig wxPayConfig, String api) {

        try {
            File file = null;
            if(HttpUtil.isHttp(filePath)  || HttpUtil.isHttps(filePath) ){
                // 创建临时文件
                file =  FileUtil.createTempFile("."+ ImageUtils.getFileExtension(filePath),true);
                // 文件流copy到临时文件
                HttpUtil.downloadFile(filePath,file);
            }else{
                file = FileUtil.newFile(filePath);
            }

            if(FileUtil.isEmpty(file)){
                log.error("微信 V3Upload 上传文件格式异常 ");
                throw new RuntimeException("微信 V3Upload 上传文件格式异常");
            }

            HashMap<Object, Object> map = new HashMap<>();
            map.put("filename", file.getName());
            map.put("sha256", SecureUtil.sha256(file));
            String reqBody = JSONUtil.toJsonStr(map);

            IJPayHttpResponse response = WxPayApi.v3(
                    WxDomainEnum.CHINA.toString(),
                    api,
                    wxPayConfig.getMchId(),
                    wxPayConfig.getSerialNo(),
                    null,
                    wxPayConfig.getPrivateKeyPath(),
                    reqBody,
                    file
            );
            log.info("微信 V3Upload 上传文件 filePath :{}, 配置 :{}, 返回参数 :{}",filePath, JSONUtil.toJsonStr(wxPayConfig),JSONUtil.toJsonStr(response));

            if(response.getStatus() == WxPayExtConstant.OK){
                JSONObject respBody = JSONUtil.parseObj(response.getBody());
                Object mediaId = respBody.get("media_id");
                if(mediaId != null){
                    return mediaId.toString();
                }
            }else{
                log.error("微信 V3Upload 上传文件 filePath :{}, 配置 :{}, 返回参数 :{}",filePath, JSONUtil.toJsonStr(wxPayConfig),JSONUtil.toJsonStr(response));
                throw new RuntimeException("微信 V3Upload 上传文件异常 : "+filePath+" , "+response.getBody());
            }
        } catch (Exception e) {
            log.error("微信 V3Upload 上传文件异常", e);
            throw new RuntimeException("微信 V3Upload 上传文件异常", e);
        }
        return null;
    }

    /**
     * 多个media转url
     * @param id          logId
     * @param urlArr      资源数组
     * @param wxPayConfig  存在就转，不存在原路返回
     * @return
     */
    public List<String> getImgUrls(String id,List<String> urlArr,WxPayConfig wxPayConfig){
        List<String> urls = new ArrayList<>();
        for(int i=0; i< urlArr.size(); i++){
            if(wxPayConfig != null){
                urls.add(getImgUrlByMediaId(id+i,urlArr.get(i),wxPayConfig));
            }else{
                urls.add(urlArr.get(i));
            }
        }
        return urls;

    }

    /**
     * 单个media转url
     * @param id
     * @param mediaUrl
     * @param wxPayConfig
     * @return
     */
    public String getImgUrlByMediaId(String id,String mediaUrl, WxPayConfig wxPayConfig) {

        try {
            if(StrUtil.isEmpty(mediaUrl)){
                return null;
            }
            // 获取 mediaId
            String mediaId = StrUtil.subAfter(mediaUrl,"/",true);
            Object mediaObj = redisTemplateService.hGet(WxPayExtConstant.COMPLAINT_IMG ,id);
            if(mediaObj != null){
                return mediaObj.toString();
            }
            // 构造签名
            String authorization = WxPayKit.buildAuthorization(RequestMethodEnum.GET, "/v3/merchant-service/images/"+mediaId,wxPayConfig.getMchId(),wxPayConfig.getSerialNo(),wxPayConfig.getPrivateKeyPath(),"");
            Map<String,String> headers = new HashMap<>();
            headers.put("Authorization",authorization);
            headers.put("Accept","application/json");
            HttpRequest request = HttpUtil.createGet(mediaUrl).headerMap(headers,false);
            HttpResponse response = request.execute();
            if(response.isOk()){
                File file = FileUtil.createTempFile(".png",true);
                response.writeBody(file);

                FileInputStream input = new FileInputStream(file);
                MultipartFile multipartFile = new MockMultipartFile("file",
                        file.getName(), "image/png", input);
                CommonResult<String> commonResult = uploadService.upload(multipartFile, CommonEnum.OssEnum.IMAGE);
                if(commonResult.isSuccess()){
                    // 将media存到Redis
                    redisTemplateService.hSet(WxPayExtConstant.COMPLAINT_IMG,id,commonResult.getData(),30*24*60*60);
                    return commonResult.getData();
                }
            }else{
                throw new RuntimeException("微信 获取图片异常 : "+mediaUrl);
            }

        } catch (Exception e) {
            log.error("微信 获取图片异常", e);
            throw new RuntimeException("微信 获取图片异常", e);
        }
        return null;
    }


    public static String complaintOperateType(String code) {
        if (!StringUtils.hasText(code)) {
            return null;
        }
        /**
         * USER_CREATE_COMPLAINT: 用户提交投诉
         * USER_CONTINUE_COMPLAINT: 用户继续投诉
         * USER_RESPONSE: 用户留言
         * PLATFORM_RESPONSE: 平台留言
         * MERCHANT_RESPONSE: 商户留言
         * MERCHANT_CONFIRM_COMPLETE: 商户申请结单
         * USER_CREATE_COMPLAINT_SYSTEM_MESSAGE: 用户提交投诉系统通知
         * COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE: 投诉单发起全额退款系统通知
         * USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE: 用户继续投诉系统通知
         * USER_REVOKE_COMPLAINT: 用户主动撤诉（只存在于历史投诉单的协商历史中）
         * USER_COMFIRM_COMPLAINT: 用户确认投诉解决（只存在于历史投诉单的协商历史中）
         * PLATFORM_HELP_APPLICATION: 平台催办
         * USER_APPLY_PLATFORM_HELP: 用户申请平台协助
         * MERCHANT_APPROVE_REFUND: 商户同意退款申请
         * MERCHANT_REFUSE_RERUND: 商户拒绝退款申请, 此时操作内容里展示拒绝原因
         * USER_SUBMIT_SATISFACTION: 用户提交满意度调查结果,此时操作内容里会展示满意度分数
         * SERVICE_ORDER_CANCEL: 服务订单已取消
         * SERVICE_ORDER_COMPLETE: 服务订单已完成
         * COMPLAINT_PARTIAL_REFUNDED_SYSTEM_MESSAGE: 投诉单发起部分退款系统通知
         * COMPLAINT_REFUND_RECEIVED_SYSTEM_MESSAGE: 投诉单退款到账系统通知
         * COMPLAINT_ENTRUSTED_REFUND_SYSTEM_MESSAGE: 投诉单受托退款系统通知
         * USER_APPLY_PLATFORM_SERVICE: 用户申请平台协助
         * USER_CANCEL_PLATFORM_SERVICE: 用户取消平台协助
         * PLATFORM_SERVICE_FINISHED: 客服结束平台协助
         */
        switch (code) {
            case "USER_CREATE_COMPLAINT":
                return "用户提交投诉";
            case "USER_CONTINUE_COMPLAINT":
                return "用户继续投诉";
            case "USER_RESPONSE":
                return "用户留言";
            case "PLATFORM_RESPONSE":
                return "平台留言";
            case "MERCHANT_RESPONSE":
                return "商户留言";
            case "MERCHANT_CONFIRM_COMPLETE":
                return "商户申请结单";
            case "USER_CREATE_COMPLAINT_SYSTEM_MESSAGE":
                return "用户提交投诉系统通知";
            case "COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE":
                return "投诉单发起全额退款系统通知";
            case "USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE":
                return "用户继续投诉系统通知";
            case "USER_REVOKE_COMPLAINT":
                return "用户主动撤诉";
            case "USER_COMFIRM_COMPLAINT":
                return "用户确认投诉解决";
            case "PLATFORM_HELP_APPLICATION":
                return "平台催办";
            case "USER_APPLY_PLATFORM_HELP":
                return "用户申请平台协助";
            case "MERCHANT_APPROVE_REFUND":
                return "商户同意退款申请";
            case "MERCHANT_REFUSE_RERUND":
                return "商户拒绝退款申请";
            case "USER_SUBMIT_SATISFACTION":
                return "用户提交满意度调查结果";
            case "SERVICE_ORDER_CANCEL":
                return "服务订单已取消";
            case "SERVICE_ORDER_COMPLETE":
                return "服务订单已完成";
            case "COMPLAINT_PARTIAL_REFUNDED_SYSTEM_MESSAGE":
                return "投诉单发起部分退款系统通知";
            case "COMPLAINT_REFUND_RECEIVED_SYSTEM_MESSAGE":
                return "投诉单退款到账系统通知";
            case "COMPLAINT_ENTRUSTED_REFUND_SYSTEM_MESSAGE":
                return "投诉单受托退款系统通知";
            case "USER_APPLY_PLATFORM_SERVICE":
                return "用户申请平台协助";
            case "USER_CANCEL_PLATFORM_SERVICE":
                return "用户取消平台协助";
            case "PLATFORM_SERVICE_FINISHED":
                return "客服结束平台协助";
            default:
                return null;
        }
    }

    public static String riskOperateType(String code) {
        if (!StringUtils.hasText(code)) {
            return null;
        }
        /**
         ONE_YUAN_PURCHASES：涉嫌一元购
         MULTI_LEVEL_DISTRIBUTION_REBATE：涉嫌多级分销返利
         PROHIBITED_BUSINESS_CATEGORIES：涉嫌我司未开放类目
         CASH_ADVANCE_VIA_CREDIT_CARD：涉嫌信用卡套现
         INDUCING_USERS_TO_MAKE_PAYMENTS：涉嫌诱导支付
         FRAUD：涉嫌欺诈
         MALICIOUS_FAN_COUNT_BOOSTING：涉嫌恶意吸粉
         CROSS_CATEGORY_ACTIVITIES：涉嫌跨类目
         CROSS_CATEGORY_BUSINESS：涉嫌跨类目经营
         GAMBLING：涉嫌赌博
         LEWD_CONTENT：涉嫌色情
         UNLICENSED_PAYMENT_AND_SETTLEMENT_BUSINESS：涉嫌无证经营支付结算业务
         INVESTMENT：涉嫌投资理财
         TRANSACTION_DISPUTE：涉嫌交易纠纷
         CROSS_BORDER_USE_OF_DOMESTIC_PAYMENT_API：涉嫌境内支付接口跨境使用
         OVERSEAS_ACTIVITIES_OUTSIDE_THE_BUSINESS_SCOPE_APPROVED_BY_REGULATORY_AUTHORITIES：涉嫌境外超监管批复范围经营
         UNUSUAL_TRANSACTION：涉嫌交易异常
         UNLICENSED_BUSINESS：涉嫌无资质经营
         WEALTH_INVESTMENT：涉嫌投资理财
         AFFILIATED_TO_A_VIOLATING_ENTITY：涉嫌关联违规主体等异常风险
         INVOLVED_IN_A_JUDICIAL_CASE：涉嫌司法案件
         INCORRECT_INFORMATION_SUBMITTED：涉嫌资料异常
         APPEAL_SUCCESSFUL：申诉成功
         REPORTED_BY_OTHERS：涉嫌他人投诉举报
         VIOLATING_SMART_CATERING_ACTIVITIES：涉嫌智慧餐饮活动违规
         MORE_THAN_ONE_MERCHANT_UNDER_A_SINGLE_MERCHANT_ID：涉嫌同一商户号下挂多个商户
         CROSS_REGION_USE_OF_INTERNATIONAL_PAYMENT_API：涉嫌境外支付接口跨区域
         UNUSUAL_REAL_TIME_TRANSACTION：涉嫌实时交易异常
         UNACCEPTABLE_DOCUMENTS：涉嫌资料不合格
         LARGE_AMOUNT_TRANSACTION：涉嫌大额交易
         ALL_MERCHANTS_HAVE_CONFIRMED_THE_WILLINGNESS_TO_OPEN_AN_ACCOUNT：无交易商户未确认开户意愿
         UNCONFIRMED_WILLINGNESS_TO_OPEN_AN_ACCOUNT：未确认开户意愿
         INACTIVE_TRANSACTION：交易停滞
         OTHER_UNUSUAL_ACTIVITIES：涉嫌其它异常
         */
        switch (code) {
            case "ONE_YUAN_PURCHASES":
                return "涉嫌一元购";
            case "MULTI_LEVEL_DISTRIBUTION_REBATE":
                return "涉嫌多级分销返利";
            case "PROHIBITED_BUSINESS_CATEGORIES":
                return "涉嫌我司未开放类目";
            case "CASH_ADVANCE_VIA_CREDIT_CARD":
                return "涉嫌信用卡套现";
            case "INDUCING_USERS_TO_MAKE_PAYMENTS":
                return "涉嫌诱导支付";
            case "FRAUD":
                return "涉嫌欺诈";
            case "MALICIOUS_FAN_COUNT_BOOSTING":
                return "涉嫌恶意吸粉";
            case "CROSS_CATEGORY_ACTIVITIES":
                return "涉嫌跨类目";
            case "CROSS_CATEGORY_BUSINESS":
                return "涉嫌跨类目经营";
            case "GAMBLING":
                return "涉嫌赌博";
            case "LEWD_CONTENT":
                return "涉嫌色情";
            case "UNLICENSED_PAYMENT_AND_SETTLEMENT_BUSINESS":
                return "涉嫌无证经营支付结算业务";
            case "INVESTMENT":
                return "涉嫌投资理财";
            case "TRANSACTION_DISPUTE":
                return "涉嫌交易纠纷";
            case "CROSS_BORDER_USE_OF_DOMESTIC_PAYMENT_API":
                return "涉嫌境内支付接口跨境使用";
            case "OVERSEAS_ACTIVITIES_OUTSIDE_THE_BUSINESS_SCOPE_APPROVED_BY_REGULATORY_AUTHORITIES":
                return "涉嫌境外超监管批复范围经营";
            case "UNUSUAL_TRANSACTION":
                return "涉嫌交易异常";
            case "UNLICENSED_BUSINESS":
                return "涉嫌无资质经营";
            case "WEALTH_INVESTMENT":
                return "涉嫌投资理财";
            case "AFFILIATED_TO_A_VIOLATING_ENTITY":
                return "涉嫌关联违规主体等异常风险";
            case "INVOLVED_IN_A_JUDICIAL_CASE":
                return "涉嫌司法案件";
            case "INCORRECT_INFORMATION_SUBMITTED":
                return "涉嫌资料异常";
            case "APPEAL_SUCCESSFUL":
                return "申诉成功";
            case "REPORTED_BY_OTHERS":
                return "涉嫌他人投诉举报";
            case "VIOLATING_SMART_CATERING_ACTIVITIES":
                return "涉嫌智慧餐饮活动违规";
            case "MORE_THAN_ONE_MERCHANT_UNDER_A_SINGLE_MERCHANT_ID":
                return "涉嫌同一商户号下挂多个商户";
            case "CROSS_REGION_USE_OF_INTERNATIONAL_PAYMENT_API":
                return "涉嫌境外支付接口跨区域";
            case "UNUSUAL_REAL_TIME_TRANSACTION":
                return "涉嫌实时交易异常";
            case "UNACCEPTABLE_DOCUMENTS":
                return "涉嫌资料不合格";
            case "LARGE_AMOUNT_TRANSACTION":
                return "涉嫌大额交易";
            case "ALL_MERCHANTS_HAVE_CONFIRMED_THE_WILLINGNESS_TO_OPEN_AN_ACCOUNT":
                return "无交易商户未确认开户意愿";
            case "UNCONFIRMED_WILLINGNESS_TO_OPEN_AN_ACCOUNT":
                return "未确认开户意愿";
            case "OTHER_UNUSUAL_ACTIVITIES":
                return "交易停滞";
            case "INACTIVE_TRANSACTION":
                return "涉嫌其它异常";
            default:
                return null;
        }
    }


    /**
     * 获取平台证书配置
     * @param wxPayConfig
     * @return
     */
    public WxPlatformConfig getWxPlatformConfig(WxPayConfig wxPayConfig){
        if(StrUtil.isEmpty(wxPayConfig.getMchId())){
            return null;
        }
        Object checkPlatSerialNo = redisTemplateService.get(WxPayExtConstant.WX_PAY_PLATFORM_CERT_CHECK+wxPayConfig.getMchId());
        if(checkPlatSerialNo != null){
            WxPlatformConfig platformConfig = JSONUtil.toBean(JSONUtil.toJsonStr(checkPlatSerialNo),WxPlatformConfig.class);
            return platformConfig;
        }
        return null;
    }


    /**
     * 从平台证书获取平台序列号
     * @param wxPayConfig
     * @return
     */
    public String getPlatSerialNumber(WxPayConfig wxPayConfig) {

        if(StrUtil.isEmpty(wxPayConfig.getMchId())){
            return null;
        }
        WxPlatformConfig platformConfig = getWxPlatformConfig(wxPayConfig);
        if(platformConfig != null){
            wxPayConfig.setPlatformCertPath(platformConfig.getPlatformCertPath());
            return platformConfig.getPlatformSerialNo();
        }
        // 通过证书地址获取证书
        X509Certificate certificate = null;
        // 获取默认的平台证书，可能没有生成在本地
        boolean exsit = FileUtil.exist(wxPayConfig.getPlatformCertPath());
        if(exsit){
            certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayConfig.getPlatformCertPath()));
            String platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
            // 提前两天检查证书是否有效
            boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayConfig.getMchId(), -2);
            log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
            // 获取新平台证书号
            if(isValid){
                platformConfig = v3GetPlatSerialNumber(wxPayConfig);
            }else{
                platformConfig = new WxPlatformConfig();
                platformConfig.setPlatformSerialNo(platSerialNo);
                platformConfig.setPlatformCertPath(wxPayConfig.getPlatformCertPath());
            }
        }else{
            platformConfig = v3GetPlatSerialNumber(wxPayConfig);
        }
        if(platformConfig == null){
            log.error("获取平台序列号失败，验签失败, wxPayConfig: {} " ,JSONUtil.toJsonStr(wxPayConfig));
            return null;
        }

        wxPayConfig.setPlatformCertPath(platformConfig.getPlatformCertPath());
        redisTemplateService.set(WxPayExtConstant.WX_PAY_PLATFORM_CERT_CHECK+wxPayConfig.getMchId(),platformConfig,1, TimeUnit.DAYS);
        return platformConfig.getPlatformSerialNo();
    }

    /**
     * 获取平台证书
     * @param wxPayConfig
     */
    public WxPlatformConfig v3GetPlatSerialNumber(WxPayConfig wxPayConfig){
        // 获取平台证书列表
        try {
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    OtherApiEnum.GET_CERTIFICATES.getUrl(),
                    wxPayConfig.getMchId(),
                    wxPayConfig.getSerialNo(),
                    null,
                    wxPayConfig.getPrivateKeyPath(),
                    "",
                    AuthTypeEnum.RSA.getCode()
            );
            if (response.getStatus() == WxPayExtConstant.OK) {
                // 平台地址取私钥证书地址前缀
                String platformCertPath = null;
                if(active.equals("local")){
                    // 判断是否带盘符
                    if(wxPayConfig.getPrivateKeyPath().contains("\\")){
                        platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"\\",true)+"\\"+ IdUtil.fastSimpleUUID()+"_" +WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                    }else{
                        platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"/",true)+"/"+ IdUtil.fastSimpleUUID() +"_"+WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                    }
                }else{
                    platformCertPath = StrUtil.subBefore(wxPayConfig.getPrivateKeyPath(),"/",true)+"/"+ IdUtil.fastSimpleUUID()+"_" +WxPayExtConstant.WX_PLATFORM_CERT_NAME;
                }
                JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
                JSONArray dataArray = jsonObject.getJSONArray("data");
                // 默认认为只有一个平台证书 获取最新得到
                JSONObject encryptObject = dataArray.getJSONObject(0);
                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
                String associatedData = encryptCertificate.getStr("associated_data");
                String cipherText = encryptCertificate.getStr("ciphertext");
                String nonce = encryptCertificate.getStr("nonce");
                String algorithm = encryptCertificate.getStr("algorithm");
                String serialNo = encryptObject.getStr("serial_no");
                final String platSerialNo = savePlatformCert(wxPayConfig.getApiV3Key(),associatedData, nonce, cipherText, algorithm, platformCertPath);
                if(StrUtil.isNotEmpty(platSerialNo)){
                    WxPlatformConfig wxPlatformConfig = new WxPlatformConfig();
                    wxPlatformConfig.setPlatformSerialNo(platSerialNo);
                    wxPlatformConfig.setPlatformCertPath(platformCertPath);
                    return wxPlatformConfig;
                }
                return null;
            }else{
                log.error("获取平台证书列表失败, {}", JSONUtil.toJsonStr(response));
            }
        } catch (Exception e) {
            log.error("获取平台证书列表异常", e);
        }
        return null;
    }

    /**
     *  保存微信平台证书
     * @param v3Key
     * @param associatedData
     * @param nonce
     * @param cipherText
     * @param algorithm
     * @param certPath
     * @return
     */
    private String savePlatformCert(String v3Key, String associatedData, String nonce, String cipherText, String algorithm, String certPath) {
        try {
            String publicKey = null;
            if (StrUtil.equals(algorithm, AuthTypeEnum.SM2.getPlatformCertAlgorithm())) {
                publicKey = PayKit.sm4DecryptToString(v3Key, cipherText, nonce, associatedData);
            } else {
                AesUtil aesUtil = new AesUtil(v3Key.getBytes(StandardCharsets.UTF_8));
                // 平台证书密文解密
                // encrypt_certificate 中的  associated_data nonce  ciphertext
                publicKey = aesUtil.decryptToString(
                        associatedData.getBytes(StandardCharsets.UTF_8),
                        nonce.getBytes(StandardCharsets.UTF_8),
                        cipherText
                );
            }
            if (StrUtil.isNotEmpty(publicKey)) {
                // 保存证书
                FileUtil.writeString(publicKey,certPath, Charset.defaultCharset());
                // 获取平台证书序列号
                X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
                return certificate.getSerialNumber().toString(16).toUpperCase();
            }
            return null;
        } catch (Exception e) {
            log.error("保存平台证书异常", e);
            return null;
        }
    }




    public Integer foramtBoolean(Boolean value){
        if(value == null){
            return 0;
        }
        if(value){
            return 1;
        }
        return 0;
    }


    /**
     * 公钥加密
     * @param text
     * @param publicKey
     * @return
     * @throws IllegalBlockSizeException
     * @throws IOException
     */
    public String rsaEncryptOAEP(String text, PublicKey publicKey)
            throws IllegalBlockSizeException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] data = text.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的公钥", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }

    public static void main(String[] args) throws IllegalBlockSizeException, IOException {
        String path = "E:/work/local_file/secret/20250318/pub_key.pem";
        // String text = rsaEncryptOAEP("name",path);
        // System.out.println(text);
        // X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(platPath));
        // String platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
        // System.out.println(platSerialNo);
    }

}
